home *** CD-ROM | disk | FTP | other *** search
- /*
- * File Name: sample JPEGConverterLib.c
- *
- * Description: This file contains the beginnings of a low level JPEG downloader.
- *
- * Written by: David Gelphman 4/3/98
- *
- * Copyright: © 1998 by Apple Computer Inc., all rights reserved.
- *
- *
- * Change History (most recent first):
- *
- * 04/03/98 DMG457 Initial version.
- */
-
- #include <Types.h>
- #include <Resources.h>
- #include <TextUtils.h>
- #include <Errors.h>
- #include <FixMath.h>
-
- #include "Debug.h"
- #include "DownloadMgrLib.h"
- #include "sample JPEGConverterLib.h"
- #include "DownloadMgrLowConverter.h"
- #include "Version.h"
- #include "sample ConverterShell.h"
- #include "Utilities.h"
- #include "PSWriterErr.h"
-
- /********* Defines *********/
-
- #define JPEGBUFFERSIZE 0x4000 // size for buffer allocations for reading data to convert
-
- #define kNumHandledTypes 3 // number of types that the sample JPEG converter handles
-
-
- /* This sample converter doesn't need to peek but there is code below to demonstrate how we'd save any
- data collected upon peek and how we'd access it later.
- */
-
- #define doSamplePeek 0 //To exercise the code, change doSamplePeek to be 1.
-
- #if doSamplePeek
- /* my typedefs for my private hint data */
- typedef Boolean hintMyPrivateVar;
- typedef SInt32 hintMyPrivate2Var;
-
- /* the tag and id for my private data hints */
- #define kHintMyPrivateTag 'tag1'
- #define kHintMyPrivateTag2 'tag2'
- #define kHintMyPrivateId 1 // both kHintMyPrivateTag, and kHintMyPrivateTag2 will use the same ID
-
- #define kHintMyPrivateDefault false // my default values for these hints should they not be present
- #define kHintMyPrivate2Default -1
-
- /*
- The signature used here is reserved by Apple. This data MUST be your application private signature in
- order to ensure that there are no collisions with other software.
- */
- #define myPrivateAppSignatureID 'vgrd'
-
- #endif
-
-
- /********* Typedefs *********/
-
- /* We need these type definitions because ConverterService is declared as:
-
- typedef struct MyConverterService{
- UInt32 nTypes;
- ConverterTypeInfo typeInfo[1];
- }MyConverterService;
-
- but for this converter we have an array of kNumHandledTypes typeInfo structures in our ConverterService.
- The Download Manager is expecting this to be an array of an unknown number of ConverterTypeInfo structures,
- but we have to tell the compiler here the correct number so we can initialize "gTheConverterDescription" correctly.
- */
-
- typedef struct MyConverterService{
- UInt32 nTypes;
- ConverterTypeInfo typeInfo[kNumHandledTypes];
- }MyConverterService;
-
- typedef struct MyConverterDescription {
- OSType converterDescSignature;
- ConverterDescVersion converterDescVersion;
- ConverterType converterType;
- MyConverterService converterService;
- }MyConverterDescription;
-
- // our client data for converting the data
- typedef struct JPEGConverterData{
- SInt16 imageheight;
- SInt16 imagewidth;
- SInt16 depth;
- SInt16 numComponents;
- Boolean doBinary;
- Boolean doLevel2;
- unsigned char *outDataBuffer;
- UInt32 outBufferSize;
- unsigned char *backChannelDataBuffer;
- SInt32 backChannelDataBufferSize;
- PSSerialStream *readStream;
- PSStream *streamIn;
- PSStream *streamOut;
- }JPEGConverterData;
-
- // for ASCII85 handling
- typedef struct PackBitsState{
- unsigned long edgeBytes;
- int edgeCount;
- }PackBitsState;
-
- typedef void (*compressionProcPtr)(Ptr *src, Ptr *dst, long count);
-
- /********** Prototypes ************/
-
- // JPEG data scanning routines
- static char *MarkerDetect(char *data,short *width,short *height,long *hRes,long *vRes,short *depth, long validBytes);
- static void SwallowQuantTable(char *data, char *bufferEnd);
- static void SwallowHuffTable(char *data, char *bufferEnd);
- static OSStatus GetJPEGImageBounds(unsigned char *theDataPtr, short *width, short *height, short *depth,
- long *hRes, long *vRes, long validDataLength);
-
- // ASCII85 output routines
- static OSStatus ASCII85Done(StreamInfoData comm, PackBitsState *bitsState);
- static OSStatus outASCII85(StreamInfoData comm, PackBitsState *bitsState, unsigned char *block, long nBytes, compressionProcPtr compPtr);
- static void noCompressionASCII85 (Ptr *src, Ptr *dst, long count);
- static StringPtr pack5(StringPtr out, unsigned long data);
-
-
- /************** Static global data ************/
-
- static const unsigned char psPrologL2[] = "\p/s{{/DeviceGray/d/DeviceRGB/DeviceCMYK}components 1 sub get setcolorspace}def\r";
-
- static const unsigned char psL2BinaryFilter[] = "\p/myfilter currentfile/DCTDecode filter def\r";
-
- static const unsigned char psL2ASCII85Filter[] = "\p/myfilter currentfile/ASCII85Decode filter/DCTDecode filter def\r";
-
- static const unsigned char psImageDictSetup1[] = "\p/iwidth ^d def/iheight ^d def/components ^d def s\r"
- "10 dict dup begin/ImageType 1 def/MultipleDataSource false def\r"
- "/Width iwidth def/Height iheight def/Decode[components{0 1}repeat]def\r";
-
- static const unsigned char psImageDictSetup2[] = "\p/ImageMatrix[iwidth 0 0 iheight neg 0 iheight]def\r"
- "/DataSource myfilter def\r"
- "/BitsPerComponent 8 def end\r";
-
- static const unsigned char psDoImage[] = "\piwidth iheight scale image "; // this must be terminated with a space, not a CR.
-
-
- // the converter description we give to the Download Manager
- MyConverterDescription gTheConverterDescription =
- {
- // signature info
- kTheConverterDescriptionSignature, // signature always first
- kInitialConverterDescriptor, // version
- // type info
- {
- "\pJPEG Downloader", // name
- "\p", // info string data comes from resource fork
- kMajorRev, kMinorRev, kReleaseStage, kNonRelease,
- },
-
- // Converter Services
- {
- kNumHandledTypes, // number of ConverterTypeInfo structures
- {
- {
- 'JPEG', // file type for JPEG data
- CANTDOWNLOAD, // priority hint - we can't download if we can't look at
- // more than the first 15 bytes of data to verify it is JPEG data we can handle
- "\p\xFF\xD8" // the first two bytes of JPEG/JFIF data
- },
- {
- 'JFIF', // file type for JFIF data
- CANTDOWNLOAD, // priority hint - we can't download if we can't look at
- // more than the first 15 bytes of data to verify it is JPEG data we can handle
- "\p\xFF\xD8" // the first two bytes of JPEG/JFIF data
- },
- {
- '????', // file type for unknown data
- CANTDOWNLOAD, // priority hint - we can't download if we can't look at
- // more than the first 15 bytes of data to verify it is JPEG data we can handle
- "\p\xFF\xD8" // the first two bytes of JPEG/JFIF data
- }
- }
- }
- };
-
- // end of global data
-
- OSStatus converterGetConverterInfoPtr(const ConverterDescription **theDescriptionPtr)
- {
- OSStatus err = noErr;
- short libRes, holdResFile;
-
- /* The reason we open the library resource fork here rather than in the library init proc
- is that we are using shared global data so our init proc only is called when the library
- is initially opened. If we were to open the library and save the
- fref, that would work for the caller who made the first call to the library, but any
- subsequent caller into the already opened library wouldn't have the resource
- fork in its resource chain and our resource calls would fail. Instead the shell code
- saves the library FSSpec and we need to open (and close) the library when we need it.
- No big deal but if we don't respect this, we'll have problems when there are multiple
- downloads going on simultaneously.
- */
-
- holdResFile = CurResFile();
- err = openLowLibraryResFile(&libRes);
- if(!err){
- // we get our info string from our resource data since it should be internationalizeable
- GetIndString(gTheConverterDescription.converterType.info, JPEGCONVERTERSTRINGS_ID, kJPEGConverterInfoString);
-
- CloseResFile(libRes);
- }
-
- if(theDescriptionPtr)*theDescriptionPtr = (ConverterDescriptionPtr)&gTheConverterDescription;
- return err;
- }
-
- OSStatus converterGetConverterName(Str255 converterName)
- {
- OSStatus err = noErr;
- short libRes, holdResFile;
-
- /* The reason we open the library resource fork here rather than in the library init proc
- is that we are using shared global data so our init proc only is called when the library
- is initially opened. If we were to open the library and save the
- fref, that would work for the caller who made the first call to the library, but any
- subsequent caller into the already opened library wouldn't have the resource
- fork in its resource chain and our resource calls would fail. Instead the shell code
- saves the library FSSpec and we need to open (and close) the library when we need it.
- No big deal but if we don't respect this, we'll have problems when there are multiple
- downloads going on simultaneously.
- */
-
- holdResFile = CurResFile();
- err = openLowLibraryResFile(&libRes);
-
- if(!err){
- // we get our converter name from our resource data since it should be internationalizeable
- GetIndString(converterName, JPEGCONVERTERSTRINGS_ID, kJPEGConverterName);
- CloseResFile(libRes);
- }
- UseResFile(holdResFile);
-
- return err;
- }
-
- OSStatus converterGetConverterDocType(PSSerialStream *readStream, PSStream *inputStream, Collection hints, OSType *theTypeP)
- {
- Unused(readStream);
- Unused(inputStream);
- Unused(hints);
-
- // We don't need to look at the data or the hints collection to know what type of data we have
- // If we get to this point we have our data type
-
- if(theTypeP){
- *theTypeP = kJPEGConverterDocType;
- }
-
- return noErr;
- }
-
-
- OSStatus converterGetVersion(struct CFMVersion *version)
- {
- version->definition = lowJPEGConverterVersionCurrent;
- version->implementation = lowJPEGConverterVersionImplementation;
- version->current = lowJPEGConverterVersionDefinition;
-
- return noErr;
- }
-
- // no need to add additional queries than the ones the shell code does for us
- OSStatus converterAddAdditionalQueries(Collection hints, Collection query)
- {
- Unused(hints);
- Unused(query);
-
- return noErr;
- }
-
- #define CANDOWNLOAD_BUFFER_SIZE JPEGBUFFERSIZE
- OSStatus converterCanConvert(PSSerialStream *readStream, PSStream *stream, Collection hints, LowConverterInfo *dataInfo, Fixed *downloadability)
- {
- OSStatus err = noErr;
- unsigned char *theData;
- SInt32 size;
- Boolean needLevel1 = true;
-
- Unused(dataInfo);
-
- *downloadability = CANTDOWNLOAD; // start by saying we can't download this file
-
- if(!err){
- // check if we know anything about the PostScript language level this job is targetted to
- kHintLanguageLevelVar langlevel = kHintLanguageLevelDef;
- err = getHint(hints, kHintLanguageLevelTag, kHintLanguageLevelId, sizeof(langlevel), &langlevel);
- if(err == collectionItemNotFoundErr)err = noErr;
-
- if(!err){
- // mustDoLevel1 macro lets us know if the language level requires us to do level 1
- needLevel1 = mustDoLevel1(langlevel);
- }
-
- }
-
- // we *might* be able to handle the data if the caller doesn't require Level 1
- if(!err && !needLevel1){
-
- theData = (unsigned char *)NewPtr(CANDOWNLOAD_BUFFER_SIZE);
- size = CANDOWNLOAD_BUFFER_SIZE;
- if(theData){
- err = readStream->read(stream, (Ptr)(theData), &size); // call the readStream input to get the first buffer of data
- if(err == eofErr)err = noErr; // eof errors are OK. We'll just use the data we got
-
- if(!err){
- short width, height, depth;
- long hRes = 72L<<16 , vRes = 72L<<16;
-
- err = GetJPEGImageBounds(theData, &width, &height, &depth, &hRes, &vRes, size);
- if(!err){
- if((depth == 8 || depth == 24) && width && height){
- *downloadability = BESTPRIORITY;
- }/*
- else
- Assert(...);
- */
- }
- err = noErr; // we can't handle the data, sorry. We don't want to return an error
- // if we simply can't handle the data
- }
- psDisposePtr((Ptr)theData);
- }else
- err = noErr; // we can't handle the data, sorry. We don't want to return any error
- // if we simply can't handle the data
- }
-
- return err;
- }
- #undef CANDOWNLOAD_BUFFER_SIZE
-
-
- OSStatus converterPeekConvert(PSSerialStream *readStream, PSStream *inputStream, Collection hints)
- {
- OSStatus err = noErr;
- Unused(readStream);
- Unused(inputStream);
- Unused(hints);
-
- #if doSamplePeek
- // this code isn't executed but is simply here to demonstrate how a converter would save any peeked data
- // it collected. The tag/id values it uses for its private collection are up to it.
- if(!err){
- Collection privateHints = NewCollection();
- if(privateHints){
- hintMyPrivateVar myData = kHintMyPrivateDefault;
- err = AddCollectionItem(privateHints, kHintMyPrivateTag, kHintMyPrivateId, sizeof(myData), &myData);
- if(!err){
- hintMyPrivate2Var myData2 = kHintMyPrivate2Default;
- err = AddCollectionItem(privateHints, kHintMyPrivateTag2, kHintMyPrivateId, sizeof(myData2), &myData2);
- }
- // ... add all of our hints as we need to
-
- // if we have no errors after adding all our hints, then lets add our private hints to the public
- // hints for use later in doConvert
- if(!err){
- Handle myCollectionHdl = NewHandle(0);
- if(myCollectionHdl){
- // we have to flatten our collection before adding it to the hints
- err = FlattenCollectionToHdl(privateHints, myCollectionHdl);
-
- /* Now add our private hints collection to the hints collection.
- The ONLY private hint we can add to the 'hints' collection passed in here
- without possibly colliding with other hints in that collection
- is a hint with the tag kHintAppPrivateTag and an ID which is the app signature
- DTS has assigned us.
- */
- if(!err)
- err = AddCollectionItemHdl(hints, kHintAppPrivateTag, myPrivateAppSignatureID, myCollectionHdl);
-
- // dispose of the handle even if there were errors since we allocated it
- DisposeHandle(myCollectionHdl);
- }else
- err = MemError();
- }
- DisposeCollection(privateHints); // dispose of our collection now that we are done with it
- }else
- err = MemError();
- }
- #endif
-
- return err;
- }
-
- #define DOWNLOAD_BUFFER_SIZE JPEGBUFFERSIZE
- OSStatus converterInitDoConvertClientData(void **clientData, PSSerialStream *readStream, PSStream *streamIn,
- PSStream *streamOut, Collection hints,
- unsigned char *backChannelDataBuffer, SInt32 backChannelDataBufferSize,
- UInt32 *languageLevel, Boolean doBinary, Boolean canDoGrayOnHost,
- Boolean isNotEPS)
- {
- OSStatus err = noErr;
- JPEGConverterData *myData = NULL;
- Unused(hints);
- Unused(isNotEPS);
- Unused(canDoGrayOnHost);
-
- // If we'd collected data on peeking, here is how we'd obtain it.
- #if doSamplePeek
- // this code isn't executed but is simply here to demonstrate how a converter would save any peeked data
- // it collected. The tag/id values it uses for its private collection are up to it.
- if(!err){
- /* here we assign default values for the data we are going to get from our hints. If our
- private collection is not available or our hints aren't available, we want to be able
- to proceed. This would be the case if we didn't get a chance to peek at the data.
- */
- hintMyPrivateVar myData = kHintMyPrivateDefault;
- hintMyPrivate2Var myData2 = kHintMyPrivate2Default;
-
- Collection privateHints = NewCollection();
- if(privateHints){
- Handle myCollectionHdl = NewHandle(0);
- if(myCollectionHdl){
- // get our flattened collection from our private hint we added. Be prepared for it to not exist!
- err = GetCollectionItemHdl(hints, kHintAppPrivateTag, myPrivateAppSignatureID, myCollectionHdl);
- if(!err){
- // we have to unflatten our collection before we can look at our hints
- err = UnflattenCollectionFromHdl(privateHints, myCollectionHdl);
- if(!err){
- err = getHint(privateHints, kHintMyPrivateTag, kHintMyPrivateId, sizeof(myData), &myData);
- if(err == collectionItemNotFoundErr){
- err = noErr; // we'll use the defaults we set above
- }
- if(!err){
- err = getHint(privateHints, kHintMyPrivateTag2, kHintMyPrivateId, sizeof(myData2), &myData2);
- if(err == collectionItemNotFoundErr){
- err = noErr; // we'll use the defaults we set above
- }
- }
- // ... get all of our hints as we need to
-
- }
- }else{
- // if our private hint is not available. This would happen if peek weren't called
- if(err == collectionItemNotFoundErr){
- err = noErr; // we can continue if our hint isn't there. We'll get the defaults
- // we set above
- }
- }
- DisposeHandle(myCollectionHdl);
- }else
- err = MemError();
-
- DisposeCollection(privateHints);
- }
- }
- #endif
-
- myData = (JPEGConverterData *)psNewPtr(sizeof(JPEGConverterData));
- if(myData){
- unsigned char *outDataBuffer = NULL;
-
- bzero(myData, sizeof(JPEGConverterData));
-
- myData->doBinary = doBinary;
- myData->doLevel2 = *languageLevel > 1; // can we generate L2 output?
-
- // We MUST fill in the caller's languagelevel with what we intend to generate
- *languageLevel = myData->doLevel2 ? 2 : 1;
-
- myData->readStream = readStream;
- myData->streamIn = streamIn;
- myData->streamOut = streamOut;
- myData->backChannelDataBuffer = backChannelDataBuffer;
- myData->backChannelDataBufferSize = backChannelDataBufferSize;
-
- myData->outDataBuffer = outDataBuffer = (unsigned char *)psNewPtr(DOWNLOAD_BUFFER_SIZE);
-
- if(outDataBuffer){
- SInt32 readBytes;
- short width, height, depth, numComponents;
- long hRes = 72L<<16 , vRes = 72L<<16;
-
- readBytes = DOWNLOAD_BUFFER_SIZE;
- err = readStream->read(streamIn, (Ptr)(outDataBuffer), &readBytes); // call the readStream input to get the first 15 data bytes
-
- // make sure we mark our buffer size to be either the download buffer size or the number of bytes
- // read on the first read
- myData->outBufferSize = readBytes;
- if(err == eofErr)err = noErr; // eof errors are OK. We'll just use the data we got
-
- if(!err){
- err = GetJPEGImageBounds(outDataBuffer, &width, &height, &depth, &hRes, &vRes, readBytes);
- if(!err){
- switch(depth){
- case 24:
- numComponents = 3;
- break;
- case 8:
- numComponents = 1;
- break;
- default:
- err = -1;
- }
- }
- if(err || !((depth == 8 || depth == 24) && width && height)){
- // this is a failure case. We can't download the data so we'll generate a log message and
- // mark it as LOGERROR instead of LOGWARNING.
-
- // note that this case shouldn't happen under normal operation since we checked the
- // data before in our converterCanConvert routine, but we'll log it
- // if it does happen
- err = writeLogMsg(streamOut, kSubAnon, NULL, JPEGCONVERTERSTRINGS_ID, kJPEGBadDataMsg, LOGERROR);
- // We must return an error here to stop the download
- // we need to return THIS error, even of writeLogMsg returns an error
- err = errCantHandleThisDownloadData; // we can't handle the data, sorry
- }
- }
-
- // if we are not doing L2, then we can't handle this data
- if(!(myData->doLevel2)){
- /* this is a failure case. We can't download the data so we'll generate a log message and
- mark it as LOGERROR instead of LOGWARNING.
-
- Note that this case shouldn't happen under normal operation since we checked the
- the language level before in our converterCanConvert routine, but we'll log it
- if it does happen. This *can* happen if the user sets up their printer and it is
- a Level 2 printer, but then they change the printer and don't re-set it up. It could
- also happen if a dumb client calls canDownload with a hints collection for one printer and
- then calls the download routine with a hints collection for a different printer.
- */
- err = writeLogMsg(streamOut, kSubAnon, NULL, JPEGCONVERTERSTRINGS_ID, kJPEGNoLevel1SupportMsg, LOGERROR);
- // We must return an error here to stop the download
- // we need to return THIS error, even of writeLogMsg returns an error
- err = errCantHandleThisDownloadData;
- }
-
-
- if(!err){
- myData->imageheight = height;
- myData->imagewidth = width;
- myData->depth = depth;
- myData->numComponents = numComponents;
- }
- }else
- err = MemError();
-
- }else
- err = MemError();
-
- // clean up after ourselves if we have an error.
- if(err){
- if(myData)
- psDisposePtr((Ptr)myData->outDataBuffer);
-
- psDisposePtr((Ptr)myData);
- myData = NULL;
- }
-
- if(clientData)
- *clientData = myData;
-
- return err;
- }
- #undef DOWNLOAD_BUFFER_SIZE
-
- OSStatus converterDisposeDoConvertClientData(void *clientData)
- {
- if(clientData){
- JPEGConverterData *jpegData = (JPEGConverterData *)clientData;
-
- psDisposePtr((Ptr)jpegData->outDataBuffer);
- psDisposePtr((Ptr)jpegData);
- }
- return noErr;
- }
-
-
- OSStatus converterGetBBox(kHintEPSBBoxVar *bbox, void *clientData)
- /* Note that this is the PostScript bounding box data that we are to return. This means
- the coordinates returned should be in the default coordinate system.
-
- The bbox normally is:
- lowerleftX lowerleftY upperrightX upperrightY
-
- but we are putting this into a rect structure and that only has top, left, bottom, right
-
- therefore:
-
- top = upperrightY
- left = lowerleftX
- bottom = lowerleftY
- right = upperrightX
- */
- {
- OSStatus err = noErr;
- JPEGConverterData *jpegData = (JPEGConverterData *)clientData;
-
- bbox->top = jpegData->imageheight;
- bbox->right = jpegData->imagewidth;
- bbox->left = bbox->bottom = 0;
-
- return err;
- }
-
-
- OSStatus converterEmitProlog(StreamInfoData comm, void *clientData)
- {
- OSStatus err = noErr;
- JPEGConverterData *jpegData = (JPEGConverterData *)clientData;
-
- err = psOutPStr(comm, psPrologL2);
-
- // check for data coming up backchannel
- if(!err)err = ReadWriteBackChannel(jpegData->streamIn, jpegData->readStream->write, jpegData->streamOut,
- jpegData->streamOut->u.ps.read,
- jpegData->backChannelDataBuffer, jpegData->backChannelDataBufferSize);
-
- return err;
- }
-
- OSStatus converterEmitPageData(StreamInfoData comm, void *clientData)
- {
- OSStatus err = noErr;
- JPEGConverterData *jpegData = (JPEGConverterData *)clientData;
-
- // we use this boolean since we've already read the first data buffer during the conversion phase
- // in our converterInitDoConvertClientData routine and we can simply use that. This allows us
- // to operate without having to rewind the stream.
- Boolean notFirstBuffer = false;
-
- short width = jpegData->imagewidth, height = jpegData->imageheight;
- short depth = jpegData->depth, numComponents = jpegData->numComponents;
- Boolean doBinary = jpegData->doBinary;
- Boolean doLevel2 = jpegData->doLevel2;
-
- unsigned char *outDataBuffer = jpegData->outDataBuffer;
-
- PSSerialStream *readStream = jpegData->readStream;
- PSStream *streamIn = jpegData->streamIn;
- PSStream *streamOut = jpegData->streamOut;
- UInt32 outBufferSize = jpegData->outBufferSize;
- UInt32 backChannelBufferSize = jpegData->backChannelDataBufferSize;
- unsigned char *backChannelBuffer = jpegData->backChannelDataBuffer;
- SInt32 readBytes = outBufferSize;
-
- if(jpegData->doLevel2){
- PackBitsState bitsState;
-
- bitsState.edgeBytes = bitsState.edgeCount = 0;
-
- if(!err)err = psOutPStr(comm, jpegData->doBinary ? psL2BinaryFilter: psL2ASCII85Filter);
-
- if(!err)err = psOutFormat(comm, psImageDictSetup1, width, height, numComponents);
- if(!err)err = psOutPStr(comm, psImageDictSetup2);
- if(!err)err = psOutPStr(comm, psDoImage);
-
- do{
- if(notFirstBuffer){
- readBytes = outBufferSize;
- err = readStream->read(streamIn, outDataBuffer, &readBytes); // call the stream input to get the first data
- if(err == eofErr)err = noErr; // we'll ignore EOF errors and use readBytes of zero to indicate we're done
- }else
- notFirstBuffer = true;
-
- if(!err && readBytes){
- if(doBinary)
- err = psOutBlock(comm, outDataBuffer, readBytes);
- else
- err = outASCII85(comm, &bitsState, outDataBuffer, readBytes, &noCompressionASCII85);
- }
-
- // check for data coming up backchannel
- if(!err)err = ReadWriteBackChannel(streamIn, readStream->write, streamOut, streamOut->u.ps.read,
- backChannelBuffer, backChannelBufferSize);
- }while(readBytes && !err);
-
- // finish the image if we are doing ascii85
- if(!err && !doBinary){
- err = ASCII85Done(comm, &bitsState);
- }
-
- // once we are done drawing the page contents we return. The shell code does the
- // showpage for us and completes the job.
- }else{
- // the !level2 case shouldn't happen here since it is checked for elsewhere but this
- // is what we are going to do if it does
- err = errCantHandleThisDownloadData;
- }
-
- return err;
- }
-
- // JPEG specific code handling
- static OSStatus GetJPEGImageBounds(unsigned char *theDataPtr, short *width, short *height, short *depth, long *hRes,
- long *vRes, long validDataLength)
- {
- OSStatus theErr = noErr;
- char *scanData;
-
- *depth = 24;
-
- // Scan the JPEG image data and find the width, height, resolution, and depth of the image
- if ( (scanData = MarkerDetect((char *)theDataPtr, width, height, hRes, vRes, depth, validDataLength)) == 0 ) {
- theErr = iNotJPEGData;
- }
-
- return theErr;
- } /* GetJPEGImageBounds */
-
-
- /*
-
-
- JPEG specific stuff. Taken almost literally from the Macintosh OS SDK.
- This is from the JFIF-PICT conversion program provided as a sample program
- in the QuickTime section of the SDK. The source is based originally on that shipped with the MacOS SDK
- for April 1997.
-
- The original change was to allow JFIF version 1.02 to be used with this code.
-
- DMG385: One significant change was to fix a bug relating to how 1 component images are handled.
- Also updated so that when it is about to read the 'data' value, it first looks to see if the
- data is within the valid bytes of data in our buffer. If not, then we're about to read outside
- the buffer and we claim that the data is not valid in that case.
-
- */
-
- /*
-
- Scan the JPEG stream for the proper markers and fill in the image parameters
-
- returns NULL if it cant comprehend the data, otherwise a pointer to the start
- of the JPEG data.
-
-
- It does a cursory check on the JPEG data to see if it's reasonable.
- Check out the ISO JPEG spec if you really want to know what's going on here.
-
- */
-
- #define invalidData(data, endBuffer) ((data) >= (endBuffer)) // DMG385
-
- static char *
- MarkerDetect(char *data,short *width,short *height,long *hRes,long *vRes,short *depth, long validBytes) // DMG385
- {
- short frame_field_length;
- short data_precision;
- short scan_field_length;
- short number_component,scan_components;
- short c1,hv1,q1,c2,hv2,q2,c3,hv3,q3;
- short dac_t1, dac_t2, dac_t3;
- unsigned char c;
- short qtabledefn;
- short htabledefn;
- short status;
- short length;
- short i;
- char *bufferEnd = data + validBytes;
-
- // check before we get started
- if(invalidData(data, bufferEnd))return (0); // DMG385
-
- c = *data++;
- qtabledefn = 0;
- htabledefn = 0;
- status = 0;
- while (c != (unsigned char)MARKER_SOS) {
-
- // check after every 'continue' of our loop
- if(invalidData(data, bufferEnd))return (0); // DMG385
-
- while (c != (unsigned char)MARKER_PREFIX){
-
- // check as we are scanning the buffer for MARKER_PREFIX
- if(invalidData(data, bufferEnd))return (0); // DMG385
-
- c = *data++; /* looking for marker prefix bytes */
- }
- while (c == (unsigned char)MARKER_PREFIX){
-
- // check as we are scanning the buffer for multiple MARKER_PREFIX
- if(invalidData(data, bufferEnd))return (0); // DMG385
-
- c = *data++; /* (multiple?) marker prefix bytes */
- }
- if (c == 0)
- continue; /* 0 is never a marker code */
-
- if (c == (unsigned char)MARKER_SOF) {
-
- frame_field_length = *(short *)data;
- data += 2;
- data_precision = *data++;
-
- if ( data_precision != 8 ) {
- status = 2;
- }
-
- *height = *(short *)data;
- data += 2;
- *width = *(short *)data;
- data += 2;
-
- number_component = *data++;
-
- switch ( number_component ) {
- case 3:
- c1 = *data++;
- hv1 = *data++;
- q1 = *data++;
- c2 = *data++;
- hv2 = *data++;
- q2 = *data++;
- c3 = *data++;
- hv3 = *data++;
- q3 = *data++;
- *depth = 24; // DMG385 changed from 32 to 24 since this is 3 component
- break;
- case 1:
- c1 = *data++;
- hv1 = *data++;
- q1 = *data++;
- *depth = 8; // DMG385 changed from 40 to 8 since this is 1 component
- break;
- default:
- status = 3;
- break;
- }
- continue;
- }
-
- if (c == (unsigned char)MARKER_SOS) {
- short tn;
- scan_field_length = *(short *)data;
- data += 2;
- scan_components = *data++;
-
- if( *depth == 8 && scan_components == 1){ // begin DMG385
- // careful to initialize these for one component case
- dac_t1 = dac_t2 = dac_t3 = 0;
- } // end DMG385
-
- // make sure that scan_components was valid since we are going to loop over it
- if(invalidData(data, bufferEnd))return (0); // DMG385
-
- for ( i=0; i < scan_components; i++ ) {
- unsigned char cn,dac_t;
-
- cn = *data++;
- dac_t = *data++;
- if ( cn == c1 ) {
- dac_t1 = dac_t;
- } else if ( cn == c2 ) {
- dac_t2 = dac_t;
- } else if ( cn == c3 ) {
- dac_t3 = dac_t;
- } else {
- status = 29;
- break;
- }
- }
- switch ( tn=(dac_t1 & 0xf) ) {
- case 0:
- case 1:
- break;
- case 0xf:
- break;
- default:
- status = 33;
- break;
- }
- switch ( tn=(dac_t2 & 0xf) ) {
- case 0:
- case 1:
- break;
- case 0xf:
- break;
- default:
- status = 33;
- break;
- }
- switch ( tn=(dac_t3 & 0xf) ) {
- case 0:
- case 1:
- break;
- case 0xf:
- break;
- default:
- status = 33;
- break;
- }
-
-
- /* Initialize the DC tables */
-
- switch ( tn=dac_t1 & 0xf0 ) {
- case 0:
- case 0x10:
- break;
- case 0xf0:
- break;
- default:
- status = 34;
- break;
- }
- switch ( tn=dac_t2 & 0xf0 ) {
- case 0:
- case 0x10:
- break;
- case 0xf0:
- break;
- default:
- status = 34;
- break;
- }
- switch ( tn=dac_t3 & 0xf0 ) {
- case 0:
- case 0x10:
- break;
- case 0xf0:
- break;
- default:
- status = 34;
- break;
- }
- if ( *data++ != 0 ) {
- // status = 18;
- }
- if ( *data++ != 63 ) {
- // status = 19;
- }
- if ( *data++ != 0 ) {
- // status = 20;
- }
- if ( status )
- return(0);
- else
- return(data);
- }
-
- if (c == (unsigned char)MARKER_DQT) {
- scan_field_length = *(short *)data;
-
- /* DMG385 removed call to SwallowQuantTable since there was no verification being done
- and the way we skipped it was to look at the size anyway
- */
- //DMG385 SwallowQuantTable(data, bufferEnd);
- data += scan_field_length;
- continue;
- }
- if (c == (unsigned char)MARKER_DHT) {
- scan_field_length = *(short *)data;
-
- /* DMG385 removed call to SwallowHuffTable since there was no verification being done
- and the way we skipped it was to look at the size anyway
- */
- //DMG385 SwallowHuffTable(data, bufferEnd);
- data += scan_field_length; // DMG385 added since we want to skip over the data and this wasn't here!
- continue;
- }
- if (c == (unsigned char)MARKER_DRI) {
- length = *(short *)data; /* read length */
- data += 2;
- length = *(short *)data;
- data += 2;
- continue;
- }
- if (c == (unsigned char)MARKER_DNL) {
- length = *(short *)data; /* read length */
- data += 2;
- length = *(short *)data;
- data += 2;
- continue;
- }
- if (c >= (unsigned char)0xD0 && c <= (unsigned char)0xD7) {
- continue;
- }
-
- if (c == (unsigned char)MARKER_SOI || c == (unsigned char)MARKER_EOI) /* image start, end marker */
- continue;
-
- if ( (c >= (unsigned char)0xC1 && c <= (unsigned char)0xcF) || (c == (unsigned char)0xde) || (c == (unsigned char)0xdf) ) {
- status = 12;
- length = *(short *)data; /* read length */
- data += length;
- continue;
- }
- if (c >= (unsigned char)MARKER_APP0 && c <= (unsigned char)0xEF) {
- length = *(short *)data; /* read length */
-
- // DMG385 Even though we get a length value, we don't loop over it so we don't need to check here.
- // We'll just wait until the next continue in the while
-
- data += 2;
- length -= 2;
- if ( (c == (unsigned char)MARKER_APP0) && length > 5 ) { /* check for JFIF marker */
- char buf[5];
- buf[0] = *data++;
- buf[1] = *data++;
- buf[2] = *data++;
- buf[3] = *data++;
- buf[4] = *data++;
- length -= 5;
-
- if ( buf[0] == 'J' && buf[1] == 'F' && buf[2] == 'I' && buf[3] == 'F' ) {
- short units;
- long xres,yres;
- short version;
-
-
- version = *(short *)data; data += 2;length -= 2;
- if ( version != 0x100 && version != 0x101 && version != 0x102 ) { // DMG - added version 102
- status = 44; // unknown JFIF version
- break;
- }
- units = *data++; length--;
- xres = *(short *)data; data += 2; length -= 2;
- yres = *(short *)data; data += 2; length -= 2;
-
- switch ( units ) {
- case 0: // no res, just aspect ratio
- *hRes = FixMul(72L<<16,xres<<16);
- *vRes = FixMul(72L<<16,yres<<16);
- break;
- case 1: // dots per inch
- *hRes = xres<<16;
- *vRes = yres<<16;
- break;
- case 2: // dots per centimeter (we convert to dpi )
- *hRes = FixMul(0x28a3d,xres<<16);
- *vRes = FixMul(0x28a3d,xres<<16);
- break;
- default:
- break;
- }
- xres = *data++; length--;
- yres = *data++; length--;
-
- /* skip JFIF thumbnail */
-
- xres *= yres;
- data += xres*3; length -= xres*3;
-
- if ( length != 0 ) {
- status = 44; // bad jfif marker
- break;
- }
- }
- }
- data += length;
- continue;
- }
- if (c == (unsigned char)MARKER_COM) {
- length = *(short *)data; /* read length */
- data += length;
- continue;
- }
- if (c >= (unsigned char)0xf0 && c <= (unsigned char)0xfd) {
- length = *(short *)data; /* read length */
- data += length;
- continue;
- }
- if ( c == 0x1 )
- continue;
- if ( (c >= (unsigned char)0x2 && c <= (unsigned char)0xbF) ) {
- length = *(short *)data; /* read length */
- status = 13;
- data += length;
- continue;
- }
- }
- return(0);
- }
-
-
- /*
- Read the quantization table from the JPEG bitstream.
- */
-
- static void
- SwallowQuantTable(char *data, char *bufferEnd) // DMG385
- {
- long i;
- long length,pm,nm;
-
- length = *(short *)data; /* read length */
-
- // make sure that length was valid since we are going to loop over it
- if(invalidData(data, bufferEnd))return; // DMG385
-
- length -= 2;
- data += 2;
- while ( length ) {
- nm= *data++; /* read precision and number */
- pm = nm>>4;
- nm &= 0xf;
- length--;
- if ( pm ) {
- for(i=0;i<64;i++) {
- length -= 2;
- data += 2;
- }
- } else {
- for(i=0;i<64;i++) {
- length--;
- data++;
- }
- }
- }
- }
-
- /*
- Read the huffman table from the JPEG bitstream.
- */
-
- static void
- SwallowHuffTable(char *data, char *bufferEnd) // DMG385
- {
- short i,tc,id;
- long length;
-
- unsigned char bin[17];
- unsigned char val[256];
-
- bin[0] = 0;
- length = *(short *)data; /* read length */
-
- // make sure that length was valid since we are going to loop over it
- if(invalidData(data, bufferEnd))return; // DMG385
-
- data += 2;
- length -= 2;
- while ( length ) {
- id=*data++; /* read id */
- length--;
- if ( id != 0 && id != 1 && id != 0x10 && id != 0x11) {
- return;
- }
- tc = 0;
- for(i=0;i<16;i++) {
- length--;
- tc += (bin[i+1] = *data++);
- }
-
- // make sure that tc was valid since we are going to loop over it
- if(invalidData(data, bufferEnd))return; // DMG385
-
- for (i=0; i < tc; i++ ) {
- length--;
- val[i] = *data++;
- }
- }
- }
-
-
- // ASCII85 handling
-
- static void noCompressionASCII85 (Ptr *src, Ptr *dst, long count)
- {
- BlockMove(*src, *dst, count);
- *src += count;
- *dst += count;
- }
-
- static OSStatus ASCII85Done(StreamInfoData comm, PackBitsState *bitsState)
- {
- unsigned long edgeBytes = bitsState->edgeBytes;
- unsigned short word = 0x800D;
- OSErr err = noErr;
- unsigned char ASCIIBuf[10], *c = ASCIIBuf;
- int edgeCount = bitsState->edgeCount;
-
- edgeBytes &= (unsigned long)-1 << 8*(4 - edgeCount);
- edgeBytes |= 0x80000000 >> 8*edgeCount;
- c = pack5(c, edgeBytes) - (3 - edgeCount);
- *c++ = '~';
- *c++ = '>';
- *c++ = '\015';
- err = psOutBlock(comm, ASCIIBuf, c - ASCIIBuf);
- bitsState->edgeCount = 0;
- bitsState->edgeBytes = 0;
-
- return err;
- }
-
- static StringPtr pack5(StringPtr c, unsigned long data) {
- unsigned long divisor;
- int n;
-
- divisor = 85L*85L*85L*85L;
- for (n = 5; n; n--) {
- *c++ = data / divisor + '!';
- data %= divisor;
- divisor /= 85;
- }
-
- return c;
- }
-
- // data for ASCII85 routines
- #define BUFFERSIZE 128
- #define ASCIIBUFFERSIZE (BUFFERSIZE*5)/4+1 /* add 1 for newline */
- static OSStatus outASCII85(StreamInfoData comm, PackBitsState *bitsState, unsigned char *block, long nBytes, compressionProcPtr compPtr)
- {
- unsigned long binBytes, binBytesCount, bytesOut, *binLongPtr;
- OSStatus err = noErr;
- unsigned char *asciiBytes;
- unsigned char *bin, binBuf[BUFFERSIZE], ASCIIBuf[ASCIIBUFFERSIZE];
- int edgeCount, edgeBytes;
-
- edgeCount = bitsState->edgeCount;
- edgeBytes = bitsState->edgeBytes;
- while (nBytes && !err) {
- if (nBytes > (BUFFERSIZE-1)-edgeCount)
- bytesOut = (BUFFERSIZE-1)-edgeCount;
- else
- bytesOut = nBytes;
- nBytes -= bytesOut;
- *(long *)binBuf = edgeBytes;
- bin = binBuf + edgeCount;
-
- (*compPtr)((Ptr *)&block, (Ptr *)&bin, bytesOut);
-
- binBytesCount = bin - binBuf;
- binLongPtr = (unsigned long *)binBuf;
- asciiBytes = ASCIIBuf;
- if (binBytesCount > 3) {
- while (binBytesCount > 3) {
- binBytes = *binLongPtr++;
- binBytesCount -= 4;
- if (binBytes == 0)
- *asciiBytes++ = 'z';
- else
- asciiBytes = pack5(asciiBytes, binBytes);
- }
- if (nBytes == 0)
- *asciiBytes++ = '\015';
- err = psOutBlock(comm, ASCIIBuf, asciiBytes - ASCIIBuf);
- }
- edgeBytes = *binLongPtr;
- edgeCount = (char)binBytesCount;
- }
- bitsState->edgeCount = edgeCount;
- bitsState->edgeBytes = edgeBytes;
-
- return err;
- }
- #undef BUFFERSIZE
- #undef ASCIIBUFFERSIZE
-
-
-